﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Web.Security;
using System.Data.Objects;
using System.Data;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels;
using System.Windows.Forms;

using GBDAAutomation;


namespace QualityStormCommon {
    public class QualityStormService : MarshalByRefObject {
        private QualityStormEntities db = new QualityStormEntities();

        /// <summary>
        /// Validates a user and creates a session in the QualityStorm Server
        /// </summary>
        /// <param name="userLogin">The User Login Name</param>
        /// <param name="userPassword">The Password</param>
        /// <returns></returns>

        #region User Methods
        public bool Login(string userLogin, string userPassword) {
            // PrintStatus("Validating User");

            string encrypted = FormsAuthentication.HashPasswordForStoringInConfigFile(userPassword, "SHA1");
            User user = db.Users.Where("it.Login=='" + userLogin + "'").First<User>();

            if (user != null) {
                if (user.Password == encrypted) {
                    UserSession session = new UserSession();
                    session.UserGuid = user.Guid;
                    session.SessionGuid = Guid.NewGuid();
                    System.Runtime.Remoting.Messaging.CallContext.SetData("SessionData", session);
                    return true;
                } else {
                    return false;
                }
            }
            return false;
        }

        public User[] GetUsers(Guid Parent) {
            var Users = from user in db.Users
                        where user.Parent == Parent
                        select user;

            return Users.ToArray<User>();
        }

        #endregion

        #region Roles Methods

        public void AddRole(Role role) {
            // Check Privileges
            if (!TestObjectPermission(db.Modules.Where("it.Name=='Roles'").First().Guid, PermissionType.Create)) {
                throw new InsufficientPrivilegesException();
            }

            var roles = from r in db.Roles.Where("it.Name=='" + role.Name + "'")
                        select r;

            if (roles.Count() > 0) {
                throw new ObjectAlreadyCreatedException();
            } else {
                db.AddToRoles(role);
            }

        }

        public Role FindRoleByName(string name) {

            return db.Roles.Where("it.Name=='" + name + "'").First<Role>();



        }

        public Role[] GetRoles() {
            return db.Roles.ToArray<Role>();
        }

        public Role[] GetRoles(Guid Parent) {
            var roles = from role in db.Roles
                        where role.Parent == Parent
                        select role;

            return roles.ToArray<Role>();
        }

        public void UpdateRole(Role updRole) {
            // Check Privileges
            if (!TestObjectPermission(db.Modules.Where("it.Name='Roles'").First().Guid, PermissionType.Modify)) {
                throw new InsufficientPrivilegesException();
            }

            // Check Duplicate Name

            var roles = from r in db.Roles
                        where r.Guid == updRole.Guid
                        select r;

            Role role = roles.First<Role>();

            role.Name = updRole.Name;
            role.Parent = updRole.Parent;

            db.SaveChanges();


        }
        public void DeleteRole(Role roleToDelete) {
            // Check Privileges
            if (!TestObjectPermission(db.Modules.Where("it.Name='Roles'").First().Guid, PermissionType.Delete)) {
                throw new InsufficientPrivilegesException();
            }

            var roles = from role in db.Roles
                        where role.Guid == roleToDelete.Guid
                        select role;
            if (roles.Count() > 0) {
                db.DeleteObject(roles.First());
                db.SaveChanges();
            }

        }

        #endregion

        #region Folder Methods

        public void AddFolder(Folder folder) {
            // Check Privileges
            if (!TestObjectPermission((Guid)folder.Module, PermissionType.Create)) {
                throw new InsufficientPrivilegesException();
            }
            // Check if there is folder with the same name under the same parent;
            // Because some LINQ bug we have to use worse code...

            // First let's get all the folders with same parent

            Folder[] folders = db.Folders.Where(f => f.Parent == folder.Parent).ToArray<Folder>();

            // let's Query the folders trying to find a duplicated name;

            if (folders.Count(f => f.Name == folder.Name) > 0) {
                throw new ObjectAlreadyCreatedException();
            } else {
                db.AddToFolders(folder);

            }

        }


        public Folder[] GetFolders(Guid Parent) {

            Guid moduleGuid = Guid.Empty;

            if (db.Folders.Count(f => f.Guid == Parent) > 0) { // is A folder 
                Folder folder = db.Folders.Where(f => f.Guid == Parent).First();
                moduleGuid = (Guid)folder.Module;

            } else if (db.Modules.Count(m => m.Guid == Parent) > 0) {
                moduleGuid = db.Modules.Where(m => m.Guid == Parent).First().Guid;
            } else {
                // It's just created folder

                return null;
            }

            // Check Privileges
            if (!TestObjectPermission(moduleGuid, PermissionType.View)) {
                throw new InsufficientPrivilegesException();
            }


            var folders = from folder in db.Folders
                          where folder.Parent == Parent
                          select folder;

            return folders.ToArray<Folder>();

        }

        public void UpdateFolder(Folder updFolder) {
            // Checking Security
            try {
                if (!TestObjectPermission((Guid)updFolder.Module, PermissionType.Modify)) {
                    throw new InsufficientPrivilegesException();
                }
                // Checking for duplicate folder name under the same parent

                Folder[] folders = db.Folders.Where(f => f.Parent == updFolder.Parent).ToArray<Folder>();
                if (folders.Count(f => f.Name == updFolder.Name && f.Guid != updFolder.Guid) > 0) {
                    throw new ObjectAlreadyCreatedException();
                }

                Folder folder = folders.Where(f => f.Guid == updFolder.Guid).First();

                folder.Name = updFolder.Name;
                folder.Parent = updFolder.Parent;

                db.SaveChanges();
            } catch (Exception e) {
                throw e;

            }

        }
        #endregion

        #region Other

        public void SetObjectPermission(Guid ObjectGuid, Role role, Permission ViewPermission,
            Permission CreatePermission, Permission ModifyPermission, Permission DeletePermission) {

            var permissions = from permission in db.ObjectPermissions
                              where permission.ObjectGuid == ObjectGuid && permission.RoleGuid == role.Guid
                              select permission;

            ObjectPermission perm = null;
            if (permissions.Count() > 0) {
                perm = permissions.First<ObjectPermission>();

            } else {
                perm = ObjectPermission.CreateObjectPermission(ObjectGuid, role.Guid);
                db.AddToObjectPermissions(perm);

            }
            perm.View = (byte)ViewPermission;
            perm.Create = (byte)CreatePermission;
            perm.Modify = (byte)ModifyPermission;
            perm.Delete = (byte)DeletePermission;

            db.SaveChanges();



        }

        public Module[] GetModules() {
            return db.Modules.ToArray<Module>();
        }

        public ObjectPermission[] GetObjectPermissions(Guid objectGuid, Role role) {
            var permissions = from permission in db.ObjectPermissions
                              where permission.RoleGuid == role.Guid && permission.ObjectGuid == objectGuid
                              select permission;




            return permissions.ToArray<ObjectPermission>();

        }

        public void AddUser(User user) {

            // Check Privileges
            if (!TestObjectPermission(db.Modules.Where("it.Name=='Users'").First().Guid, PermissionType.Create)) {
                throw new InsufficientPrivilegesException();
            }


            // Check Duplicate Name;
            if (db.Users.Where("it.login == '" + user.Login + "'").Count() > 0) {
                throw new ObjectAlreadyCreatedException();
            }


            db.AddToUsers(user);
            db.SaveChanges();

        }



        public bool TestObjectPermission(Guid ObjectGuid, PermissionType type) {


            UserSession session = System.Runtime.Remoting.Messaging.CallContext.GetData("SessionData") as UserSession;



            var permissions = from p in db.ObjectPermissions
                              join ur in db.UserRoles on p.RoleGuid equals ur.RoleGuid
                              where ur.UserGuid == session.UserGuid && p.ObjectGuid == ObjectGuid
                              select p;

            bool granted = false;
            bool denied = false;
            foreach (ObjectPermission permission in permissions) {
                switch (type) {
                    case PermissionType.Create:
                        if (((Permission)permission.Create) == Permission.Grant) {
                            granted = true;
                        }

                        if (((Permission)permission.Create) == Permission.Deny) {
                            denied = true;
                        }

                        break;

                    case PermissionType.View:
                        if (((Permission)permission.View) == Permission.Grant) {
                            granted = true;
                        }

                        if (((Permission)permission.View) == Permission.Deny) {
                            denied = true;
                        }

                        break;

                    case PermissionType.Modify:
                        if (((Permission)permission.Modify) == Permission.Grant) {
                            granted = true;
                        }

                        if (((Permission)permission.Modify) == Permission.Deny) {
                            denied = true;
                        }

                        break;

                    case PermissionType.Delete:
                        if (((Permission)permission.Delete) == Permission.Grant) {
                            granted = true;
                        }

                        if (((Permission)permission.Delete) == Permission.Deny) {
                            denied = true;
                        }

                        break;


                }
            }



            return granted && !denied;
        }

        private void PrintStatus(string Message) {
            System.Console.WriteLine(Message);
        }


        public void SaveChanges() {
            db.SaveChanges();
        }

        public Role[] GetUserRoles(User m_user) {
            var roles = from ur in db.UserRoles
                        join r in db.Roles on ur.RoleGuid equals r.Guid
                        where ur.UserGuid == m_user.Guid
                        select r;

            return roles.ToArray<Role>();
        }

        public void UpdateUser(User updUser) {
            // Check Security
            if (!TestObjectPermission(db.Modules.Where("it.Name='Users'").First().Guid, PermissionType.Modify)) {
                throw new InsufficientPrivilegesException();
            }

            // Check Duplicate Name

            var dupUser = db.Users.Where("it.Login=='" + updUser.Login + "'");
            if (dupUser.Count(p => p.Guid != updUser.Guid) > 0) {
                throw new ObjectAlreadyCreatedException();
            }


            var users = from u in db.Users
                        where u.Guid == updUser.Guid
                        select u;

            User user = users.First<User>();

            user.FirstName = updUser.FirstName;
            user.LastName = updUser.LastName;

            user.Login = updUser.Login;
            user.Email = updUser.Email;
            user.Telephone = updUser.Telephone;
            user.Password = updUser.Password;
            user.CellPhone = updUser.CellPhone;
            user.LockedOut = updUser.LockedOut;
            user.MaxNumberOfTrials = updUser.MaxNumberOfTrials;
            user.PasswordExpires = updUser.PasswordExpires;
            user.PasswordExpirationDays = updUser.PasswordExpirationDays;
            user.Parent = updUser.Parent;
            user.MustChangePassword = updUser.MustChangePassword;



            db.SaveChanges();

        }

        public void SetRoleToUser(string roleName, User m_user, bool chk) {
            // Find the role;

            Role role = db.Roles.Where("it.Name == '" + roleName + "'").First();
            var userRoles = from userRole in db.UserRoles
                            where userRole.RoleGuid == role.Guid && userRole.UserGuid == m_user.Guid
                            select userRole;


            if (!chk) {

                if (userRoles.Count() == 1) // there is an older record
                    db.DeleteObject(userRoles.First());
            } else {
                if (userRoles.Count() == 0) {
                    UserRole newUserRole = UserRole.CreateUserRole(m_user.Guid, role.Guid);
                    db.AddToUserRoles(newUserRole);
                    db.SaveChanges();
                }
            }
        }

        public void DeleteUser(User userToDelete) {
            // Check Privileges
            if (!TestObjectPermission(db.Modules.Where("it.Name='Users'").First().Guid, PermissionType.Delete)) {
                throw new InsufficientPrivilegesException();
            }
            var users = from user in db.Users
                        where user.Guid == userToDelete.Guid
                        select user;


            if (users.Count() > 0) {
                db.DeleteObject(users.First());
                db.SaveChanges();
            }

        }

        public Product[] GetProducts(Guid parent) {
            if (!TestObjectPermission(db.Modules.Where("it.Name='Products'").First().Guid, PermissionType.View)) {
                throw new InsufficientPrivilegesException();
            }

            var products = from product in db.Products
                           where product.Parent == parent
                           select product;

            return products.ToArray<Product>();
        }

        public void AddProduct(Product product) {
            // Check Privileges
            if (!TestObjectPermission(db.Modules.Where("it.Name=='Products'").First().Guid, PermissionType.Create)) {
                throw new InsufficientPrivilegesException();
            }


            var products = from p in db.Products.Where("it.Name=='" + product.Name + "'")
                           select p;

            if (products.Count() > 0) {
                throw new ObjectAlreadyCreatedException();
            } else {

                db.AddToProducts(product);
            }

        }

        public void UpdateProduct(Product m_Product) {
            // Check Security
            if (!TestObjectPermission(db.Modules.Where("it.Name='Products'").First().Guid, PermissionType.Modify)) {
                throw new InsufficientPrivilegesException();
            }

            // Check Duplicate Name

            var dupProd = db.Products.Where("it.Name=='" + m_Product.Name + "'");
            if (dupProd.Count(p => p.Guid != m_Product.Guid) > 0) {
                throw new ObjectAlreadyCreatedException();
            }


            var products = from p in db.Products
                           where p.Guid == m_Product.Guid
                           select p;

            Product product = products.First<Product>();
            product.Name = m_Product.Name;
            product.Parent = m_Product.Parent;
            db.SaveChanges();

        }
        #endregion

        public void DeleteProduct(Product products) {
            // Check Privileges
            if (!TestObjectPermission(db.Modules.Where("it.Name='Products'").First().Guid, PermissionType.Delete)) {
                throw new InsufficientPrivilegesException();
            }
            db.DeleteObject(db.Products.Where(p => p.Guid == products.Guid).First());

        }

        public Array GetOpcServers(string serverName) {
            GBDAAutomation.OPCServerClass server = new OPCServerClass();
            Array r = (Array)server.GetOPCServers(serverName);
            List<string> servers = new List<string>();
            foreach (string s in r) {
                servers.Add(s);
            }

            return servers.ToArray();
        }


        public void UpdateExternalConnection(ExternalConnection externalConnection) {
            // Check Security
            if (!TestObjectPermission(db.Modules.Where("it.Name='External Connections'").First().Guid, PermissionType.Modify)) {
                throw new InsufficientPrivilegesException();
            }

            // Check Duplicate Name

            var dupConn = db.ExternalConnections.Where("it.Name=='" + externalConnection.Name + "'");
            if (dupConn.Count(p => p.Guid != externalConnection.Guid) > 0) {
                throw new ObjectAlreadyCreatedException();
            }

            ExternalConnection connection = db.ExternalConnections.Where(e => e.Guid == externalConnection.Guid).First();
            connection.Name = externalConnection.Name;
            connection.OPCMachineName = externalConnection.OPCMachineName;
            connection.OPCServerName = externalConnection.OPCServerName;
            connection.Parent = externalConnection.Parent;
            connection.QualityStormLogin = externalConnection.QualityStormLogin;
            connection.QualityStormPassword = externalConnection.QualityStormPassword;
            connection.QualityStormServerName = externalConnection.QualityStormServerName;
            connection.QualityStormPort = externalConnection.QualityStormPort;
            connection.DatabaseClass = externalConnection.DatabaseClass;
            connection.DatabaseConnectionString = externalConnection.DatabaseConnectionString;
            connection.Type = externalConnection.Type;
            db.SaveChanges();

        }


        public void AddExternalConnection(ExternalConnection externalConnection) {
            var externalConnections = from e in db.ExternalConnections.Where("it.Name=='" + externalConnection.Name + "'")
                                      select e;

            if (externalConnections.Count() > 0) {
                throw new ObjectAlreadyCreatedException();
            } else {

                db.AddToExternalConnections(externalConnection);

            }


        }

        public ExternalConnection[] GetExternalConnections(Guid parent) {
            if (!TestObjectPermission(db.Modules.Where("it.Name='External Connections'").First().Guid, PermissionType.View)) {
                throw new InsufficientPrivilegesException();
            }
            if (parent != Guid.Empty) {
                var connections = from connection in db.ExternalConnections
                                  where connection.Parent == parent
                                  select connection;

                return connections.ToArray();
            } else {
                return db.ExternalConnections.ToArray();
            }
        }

        public System.Data.DataTable GetDatabaseProviders() {
            return System.Data.Common.DbProviderFactories.GetFactoryClasses();
        }




        public bool TestDatabaseConnection(string ClassName, string ConnectionString) {
            try {
                IDbConnection cnn = System.Data.Common.DbProviderFactories.GetFactory(ClassName).CreateConnection();
                cnn.ConnectionString = ConnectionString;
                cnn.Open();
                cnn.Close();
                return true;
            } catch (Exception e) {
                throw new ExternalConnectionException(e.Message);
            }

        }

        public bool TestOPCConnection(string OPCMachineName, string OPCServerName) {
            try {
                OPCServerClass opcServer = new OPCServerClass();
                opcServer.Connect(OPCServerName, OPCMachineName);
                return true;
            } catch (Exception e) {
                throw new ExternalConnectionException(e.Message);
            }

        }

        public bool TestQualityStormConnection(string QualityStormServerName, string QualityStormPort, string Login, string Password) {
            try {
                TcpChannel chan = new TcpChannel();

                ChannelServices.RegisterChannel(chan);
                // Create an instance of the remote object
                QualityStormCommon.QualityStormService service =
                 (QualityStormCommon.QualityStormService)Activator.GetObject(
                  typeof(QualityStormCommon.QualityStormService), "tcp://" + QualityStormServerName + ":" + QualityStormPort + "/QualityStormService");
                bool result = service.Login(Login, Password);
                ChannelServices.UnregisterChannel(chan);
                return result;

            } catch (Exception e) {
                throw new ExternalConnectionException(e.Message);
            }
        }

        public void AddProcessModel(ProcessModel ProcessModel) {
            // Check Privileges
            if (!TestObjectPermission(db.Modules.Where("it.Name=='Process Models'").First().Guid, PermissionType.Create)) {
                throw new InsufficientPrivilegesException();
            }


            var products = from p in db.Products.Where("it.Name=='" + ProcessModel.Name + "'")
                           select p;

            if (products.Count() > 0) {
                throw new ObjectAlreadyCreatedException();
            } else {

                db.AddToProcessModels(ProcessModel);
            }

        }

        public void UpdateProcessModel(ProcessModel ProcessModel) {
            // Check Security
            if (!TestObjectPermission(db.Modules.Where("it.Name='Process Models'").First().Guid, PermissionType.Modify)) {
                throw new InsufficientPrivilegesException();
            }

            // Check Duplicate Name

            var dupProd = db.Products.Where("it.Name=='" + ProcessModel.Name + "'");
            if (dupProd.Count(p => p.Guid != ProcessModel.Guid) > 0) {
                throw new ObjectAlreadyCreatedException();
            }


            var processModels = from p in db.ProcessModels
                                where p.Guid == ProcessModel.Guid
                                select p;

            ProcessModel process = processModels.First<ProcessModel>();
            process.Name = ProcessModel.Name;
            process.Parent = ProcessModel.Parent;
            db.SaveChanges();
        }

        public ProcessModel[] GetProcessModels(Guid parent) {
            if (!TestObjectPermission(db.Modules.Where("it.Name=='Process Models'").First().Guid, PermissionType.View)) {
                throw new InsufficientPrivilegesException();
            }
            var processModels = from processModel in db.ProcessModels
                                where processModel.Parent == parent
                                select processModel;

            return processModels.ToArray();
        }

        public OPCBranch BrowseOPCServer(ExternalConnection opcConnection) {
            OPCServer server = new OPCServerClass();
            server.Connect(opcConnection.OPCServerName, opcConnection.OPCMachineName);

            OPCBrowser browser = server.CreateBrowser();

            OPCBranch branch = new OPCBranch();
            branch.Name = "Root";

            Browse(browser, branch);




            return branch;

        }

        private void Browse(OPCBrowser browser, OPCBranch parentBranch) {
            browser.ShowLeafs(false);
            IEnumerator e = browser.GetEnumerator();
            while (e.MoveNext()) {
                OPCItem item = new OPCItem();
                item.Name = e.Current.ToString();
                parentBranch.Children.Add(item);
            }
            browser.ShowBranches();
            int i = browser.Count;
            string[] branches = new string[i + 1];
            for (int j = 1; j <= i; j++) {
                branches[j] = browser.Item(j).ToString();
            }

            foreach (string branch in branches) {
                if (branch != null) {
                    browser.MoveDown(branch);
                    OPCBranch newBranch = new OPCBranch();
                    newBranch.Name = branch;
                    parentBranch.Children.Add(newBranch);
                    Browse(browser, newBranch);
                    browser.MoveUp();
                }
            }
        }
    }
}

